package net.atomarrow.mapping;

import net.atomarrow.action.Action;
import net.atomarrow.annotation.ControllerUrl;
import net.atomarrow.annotation.Inject;
import net.atomarrow.annotation.MethodUrl;
import net.atomarrow.annotation.Url;
import net.atomarrow.aop.InterceptorKeeper;
import net.atomarrow.configs.Config;
import net.atomarrow.controllers.Controller;
import net.atomarrow.ioc.ActionContext;
import net.atomarrow.ioc.ApplicationContext;
import net.atomarrow.render.Render;
import net.atomarrow.util.StringUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
 * Action映射
 * @author Michael
 *
 */
public class ActionMapping {
    @Inject
    private InterceptorKeeper interceptorKeeper;
    private HashMap<String,Action> actionMap = new HashMap<String,Action>();

    public  Action findAction(String actionKey) {
        return actionMap.get(actionKey);
    }

    public Action findByParseRequest(HttpServletRequest request) {
        String url = request.getServletPath();
        return findByParseUrl(url,request);
    }

    public Action findByParseUrl(String url,HttpServletRequest request){
        if (url.contains(".")) {
            return null;
        }
        Action action = findAction(url);
        if (action != null) {
            return action;
        }
        int i=url.lastIndexOf("/");
        if (i != -1) {
            if (i == 0) { // 比如访问 xxx.abc.com/2 这种情况
                action = findAction("/");
            } else {
                action = findAction(url.substring(0, i));
            }
        	if(action!=null){
        		request.setAttribute(ActionContext.URLPARAM, url.substring(i + 1));
        	}
            return action;
        }
        return null;
    }
    public void loadActions(Class clazz){
    	loadActions(clazz, true);
    }
    public void loadActions(Class clazz,boolean checkDuplicate){
        Method[] methods = clazz.getDeclaredMethods();
        Action action;
        String fullPath;
        String controllerName;
        if(clazz.isAnnotationPresent(ControllerUrl.class)){
            ControllerUrl url = (ControllerUrl) clazz.getAnnotation(ControllerUrl.class);
        	controllerName=url.value();
        	if(controllerName.equals("/")){
        		controllerName="";
        	}else if(controllerName.startsWith("/")){
        		controllerName=controllerName.substring(1);
        	}
        }else{
        	controllerName=StringUtil.toLowerCaseFirstWord(clazz.getSimpleName().replaceAll("Controller", ""));
        }

        String methodName = null;
        for(Method method:methods){
            if(!Render.class.isAssignableFrom(method.getReturnType())){
                continue;
            }
            if(method.isAnnotationPresent(Url.class)){
            	Url url = method.getAnnotation(Url.class);
            	fullPath=url.value();
            	methodName=getMethodName(fullPath);
            }else{
            	if(method.isAnnotationPresent(MethodUrl.class)){
            		MethodUrl methodUrl = method.getAnnotation(MethodUrl.class);
                	methodName = methodUrl.value();
                	if(methodName.equals("/")){
                		methodName="";
                	}else if(methodName.startsWith("/")){
                		methodName=methodName.substring(1);
                	}
            	}else{
            		methodName=method.getName();
            	}
            	if(controllerName.equals("")){
            		fullPath="/"+methodName;
            	}else{
            		fullPath="/"+controllerName+"/"+methodName;
            	}
            }
            action = new Action();
            action.setControllerClass(clazz);
            action.setMethodName(methodName);
            action.setInterceptors(interceptorKeeper.getControllerInterceptors());
            action.setMethod(method);
            action.setMappingUrl(fullPath);
            putMapping(action.getMappingUrl(), action,checkDuplicate);
            if("index".equals(methodName)){
                action = new Action();
                action.setControllerClass(clazz);
                action.setMethodName(methodName);
                action.setInterceptors(interceptorKeeper.getControllerInterceptors());
                action.setMethod(method);
                action.setMappingUrl("/"+controllerName);
            	putMapping(action.getMappingUrl(), action,true);
            	if(Config.getConfig().configStartLog()){
            		System.out.println("/"+controllerName);
            	}
            }
            methodName=null;
            if(Config.getConfig().configStartLog()){
            	System.out.println(fullPath);
            }
        }
    }

    public void removeActions(Class clazz){
    	 Method[] methods = clazz.getDeclaredMethods();
         Action action;
         String fullPath;
         String controllerName;
         if(clazz.isAnnotationPresent(ControllerUrl.class)){
             ControllerUrl url = (ControllerUrl) clazz.getAnnotation(ControllerUrl.class);
         	controllerName=url.value();
         	if(controllerName.equals("/")){
         		controllerName="";
         	}else if(controllerName.startsWith("/")){
         		controllerName=controllerName.substring(1);
         	}
         }else{
         	controllerName=StringUtil.toLowerCaseFirstWord(clazz.getSimpleName().replaceAll("Controller", ""));
         }

         String methodName = null;
         for(Method method:methods){
             if(!Render.class.isAssignableFrom(method.getReturnType())){
                 continue;
             }
             if(method.isAnnotationPresent(Url.class)){
             	Url url = method.getAnnotation(Url.class);
             	fullPath=url.value();
             	methodName=getMethodName(fullPath);
             }else{
             	if(method.isAnnotationPresent(MethodUrl.class)){
             		MethodUrl methodUrl = method.getAnnotation(MethodUrl.class);
                 	methodName = methodUrl.value();
                 	if(methodName.equals("/")){
                 		methodName="";
                 	}else if(methodName.startsWith("/")){
                 		methodName=methodName.substring(1);
                 	}
             	}else{
             		methodName=method.getName();
             	}
             	if(controllerName.equals("")){
             		fullPath="/"+methodName;
             	}else{
             		fullPath="/"+controllerName+"/"+methodName;
             	}
             }
             actionMap.remove(fullPath);
             if(methodName.equals("index")){
            	 actionMap.remove("/"+controllerName);
             }
         }
    }
    private String getMethodName(String fullPath) {
        if(fullPath.equals("/")){
            return "";
        }
        if(fullPath.endsWith("/")){
            fullPath=fullPath.substring(0,fullPath.length()-1);
        }
        int index = fullPath.lastIndexOf("/");
        return fullPath.substring(index+1);
    }

    private void putMapping(String path,Action action,boolean checkDuplicate){
    	Object obj = actionMap.get(path);
    	if(checkDuplicate&&obj!=null){
    		throw new RuntimeException(path+" config duplicate");
    	}
    	actionMap.put(path, action);
    }

    public void LoadControllers(List<String> paths){

        Class clazz;
        try {
            for (String path : paths) {
                clazz = Class.forName(path);
                if(Controller.class.isAssignableFrom(clazz)){
                    loadActions(clazz);
                    ApplicationContext.load(clazz.newInstance());
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }
    public List<String> getAllMappingPath(){
        List<String> paths = new ArrayList<String>();
        Iterator<String> iter = actionMap.keySet().iterator();
        while(iter.hasNext()){
            paths.add(iter.next());
        }
        return paths;
    }
}
